package main

import (
	"bufio"
	"fmt"
	// log "github.com/alecthomas/log4go"
	"errors"
	"io"
	"net"
	"strconv"
	// "time"
)

const (
	minCmdNum = 1
	maxCmdNum = 5
)

var (
	// cmd parse failed
	ErrProtocol = errors.New("cmd format error")
)

// tcpBuf cache.
type tcpBufCache struct {
	instance []chan *bufio.Reader
	round    int
}

// newTCPBufCache return a new tcpBuf cache.
func newtcpBufCache() *tcpBufCache {
	inst := make([]chan *bufio.Reader, 0, Conf.BufioInstance)
	fmt.Printf("create %d read buffer instance\n", Conf.BufioInstance)
	for i := 0; i < Conf.BufioInstance; i++ {
		inst = append(inst, make(chan *bufio.Reader, Conf.BufioNum))
	}
	return &tcpBufCache{instance: inst, round: 0}
}

// Get return a chan bufio.Reader (round-robin).
func (b *tcpBufCache) Get() chan *bufio.Reader {
	rc := b.instance[b.round]
	// split requets to diff buffer chan
	if b.round++; b.round == Conf.BufioInstance {
		b.round = 0
	}
	return rc
}

// newBufioReader get a Reader by chan, if chan empty new a Reader.
func newBufioReader(c chan *bufio.Reader, r io.Reader) *bufio.Reader {
	select {
	case p := <-c:
		p.Reset(r)
		return p
	default:
		fmt.Println("tcp bufioReader cache empty")
		return bufio.NewReaderSize(r, Conf.RcvbufSize)
	}
}

// putBufioReader pub back a Reader to chan, if chan full discard it.
func putBufioReader(c chan *bufio.Reader, r *bufio.Reader) {
	r.Reset(nil)
	select {
	case c <- r:
	default:
		fmt.Println("tcp bufioReader cache full")
	}
}

// StartTCP Start tcp listen.
func StartTCPService() error {
	for _, bind := range Conf.TCPBind {
		fmt.Printf("start tcp listen addr:\"%s\"\n", bind)
		go tcpListen(bind)
	}
	return nil
}

func tcpListen(bind string) {
	addr, err := net.ResolveTCPAddr("tcp", bind)
	if err != nil {
		fmt.Println("net.ResolveTCPAddr(\"tcp\"), %s) error(%v)", bind, err)
		panic(err)
	}
	fmt.Println(addr)
	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		fmt.Println("net.ListenTCP(\"tcp4\", \"%s\") error(%v)", bind, err)
		panic(err)
	}
	// free the listener resource
	defer func() {
		fmt.Println("tcp addr: \"%s\" close", bind)
		if err := l.Close(); err != nil {
			fmt.Println("listener.Close() error(%v)", err)
		}
	}()

	// init reader buffer instance
	rb := newtcpBufCache()
	for {
		fmt.Println("start accept")
		conn, err := l.AcceptTCP()
		fmt.Println("Accepted!")
		if err != nil {
			fmt.Println("listener.AcceptTCP() error(%v)", err)
			continue
		}
		if err = conn.SetKeepAlive(Conf.TCPKeepalive); err != nil {
			fmt.Println("conn.SetKeepAlive() error(%v)", err)
			conn.Close()
			continue
		}
		if err = conn.SetReadBuffer(Conf.RcvbufSize); err != nil {
			fmt.Println("conn.SetReadBuffer(%d) error(%v)", Conf.RcvbufSize, err)
			conn.Close()
			continue
		}
		if err = conn.SetWriteBuffer(Conf.SndbufSize); err != nil {
			fmt.Println("conn.SetWriteBuffer(%d) error(%v)", Conf.SndbufSize, err)
			conn.Close()
			continue
		}
		// // first packet must sent by client in specified seconds
		// if err = conn.SetReadDeadline(time.Now().Add(fitstPacketTimedoutSec)); err != nil {
		// 	log.Error("conn.SetReadDeadLine() error(%v)", err)
		// 	conn.Close()
		// 	continue
		// }
		rc := rb.Get()
		// one connection one routine
		go handleTCPConn(conn, rc)
		fmt.Println("accept finished")
	}
}

//	下面的是协议层的东西

// hanleTCPConn handle a long live tcp connection.
func handleTCPConn(conn net.Conn, rc chan *bufio.Reader) {
	conn.Write([]byte("逗逼远翔公！现在服务器能用啦，简单用法:发tcp报文 '1|<用户名>|<密码>|' (不带单引号)来注册!注册即送远神香吻一个～\n"))
	// 先写出conn的来源ip，如果不再白名单上就直接断了

	// 然后parse命令
	// 定义命令

	// Signup		1
	// AuthLogin	2
	// Logout		3
	// Modify		4
	// Acquire		5

	addr := conn.RemoteAddr().String()
	fmt.Println("<%s> handleTcpConn routine start", addr)
	rd := newBufioReader(rc, conn)

	// 原来的代码，现在协议层重写

	// if args, err := parseCmd(rd); err == nil {
	// 	// return buffer bufio.Reader
	// 	putBufioReader(rc, rd)
	// 	switch args[0] {
	// 	case "sub":
	// 		SubscribeTCPHandle(conn, args[1:])
	// 		break
	// 	default:
	// 		conn.Write(ParamReply)
	// 		log.Warn("<%s> unknown cmd \"%s\"", addr, args[0])
	// 		break
	// 	}
	// } else {
	// 	// return buffer bufio.Reader
	// 	putBufioReader(rc, rd)
	// 	log.Error("<%s> parseCmd() error(%v)", addr, err)
	// }

	cmd0, err := rd.ReadBytes('|');
	if err != nil{
		// 链接有问题，赶紧终止
		fmt.Println("出了个小问题:", err)
		conn.Close()
		return
	}
	fmt.Println(cmd0)

	switch cmd, _ := strconv.Atoi(string(cmd0[:1])); cmd {
	case C_Signup:
		fmt.Println("收到用户注册命令")
		username, err := rd.ReadBytes('|')
		password, err := rd.ReadBytes('|')

		username = username[:len(username)-1]
		password = password[:len(password)-1]

		fmt.Printf("用户名:%s,密码:%s\n", string(username), string(password))

		if err != nil {
			panic("用户名密码不对！")
		}
		if err := Signup(&Userinfo{UUID: "5", account: string(username), password: string(password)}); err != nil {
			panic(err)
		}
		conn.Write([]byte("user signedup!"))
	default:
		fmt.Printf("未知命令: %s \n", cmd)
		conn.Write([]byte("unknown command!"))
	}

	// close the connection
	if err := conn.Close(); err != nil {
		fmt.Println("<%s> conn.Close() error(%v)", addr, err)
	}
	fmt.Println("<%s> handleTcpConn routine stop", addr)
	return
}

// parseCmd parse the tcp request command.
func parseCmd(rd *bufio.Reader) ([]string, error) {
	// get argument number
	argNum, err := parseCmdSize(rd, '*')
	if err != nil {
		fmt.Println("tcp:cmd format error when find '*' (%v)", err)
		return nil, err
	}
	if argNum < minCmdNum || argNum > maxCmdNum {
		fmt.Println("tcp:cmd argument number length error")
		return nil, ErrProtocol
	}
	args := make([]string, 0, argNum)
	for i := 0; i < argNum; i++ {
		// get argument length
		cmdLen, err := parseCmdSize(rd, '$')
		if err != nil {
			fmt.Println("tcp:parseCmdSize(rd, '$') error(%v)", err)
			return nil, err
		}
		// get argument data
		d, err := parseCmdData(rd, cmdLen)
		if err != nil {
			fmt.Println("tcp:parseCmdData() error(%v)", err)
			return nil, err
		}
		// append args
		args = append(args, string(d))
	}
	return args, nil
}

// parseCmdSize get the request protocol cmd size.
func parseCmdSize(rd *bufio.Reader, prefix uint8) (int, error) {
	// get command size
	cs, err := rd.ReadBytes('\n')
	if err != nil {
		fmt.Println("tcp:rd.ReadBytes('\\n') error(%v)", err)
		return 0, err
	}
	csl := len(cs)
	if csl <= 3 || cs[0] != prefix || cs[csl-2] != '\r' {
		fmt.Println("tcp:\"%v\"(%d) number format error, length error or prefix error or no \\r", cs, csl)
		return 0, ErrProtocol
	}
	// skip the \r\n
	cmdSize, err := strconv.Atoi(string(cs[1 : csl-2]))
	if err != nil {
		fmt.Println("tcp:\"%v\" number parse int error(%v)", cs, err)
		return 0, ErrProtocol
	}
	return cmdSize, nil
}

// parseCmdData get the sub request protocol cmd data not included \r\n.
func parseCmdData(rd *bufio.Reader, cmdLen int) ([]byte, error) {
	d, err := rd.ReadBytes('\n')
	if err != nil {
		fmt.Println("tcp:rd.ReadBytes('\\n') error(%v)", err)
		return nil, err
	}
	dl := len(d)
	// check last \r\n
	if dl != cmdLen+2 || d[dl-2] != '\r' {
		fmt.Println("tcp:\"%v\"(%d) number format error, length error or no \\r", d, dl)
		return nil, ErrProtocol
	}
	// skip last \r\n
	return d[0:cmdLen], nil
}
